#include "win_gdi.h"
#include "UIContainer.h"
#include "comm.h"
#include "win_nt.h"
#include <Windows.h>
#include <gdiplus.h>
#include <math.h>

using namespace Gdiplus;
GdiplusStartupInput m_gdiplusStartupInput;
ULONG_PTR m_gdiplusToken;

void render_init()
{
    GdiplusStartup(&m_gdiplusToken, &m_gdiplusStartupInput, NULL);
}

void render_destroy()
{
    GdiplusShutdown(m_gdiplusToken);
}

void render_start(DuiWinManager *mgr)
{
    if (mgr->hdc == nullptr)
    {
        mgr->hdc = GetDC((HWND)mgr->hWin);
        mgr->graph = nullptr;
    }
}

void render_end(DuiWinManager *mgr)
{
    if (mgr->hdc != nullptr)
    {
        if (mgr->graph != nullptr)
        {
            delete mgr->graph;
            mgr->graph = nullptr;
        }
        ReleaseDC((HWND)mgr->hWin, (HDC)mgr->hdc);
        mgr->hdc = nullptr;
    }
}

void _render_draw_child(DuiWinManager *mgr, UIContainer *pChild)
{
    if (pChild->getNeedUpdate())
    {
        pChild->onDraw(mgr);
        // pChild->update(false);
    }

    for (int i = 0; i < pChild->getChildCount(); i++)
    {
        auto pChild2 = (UIContainer *)pChild->getChild(i);
        _render_draw_child(mgr, pChild2);
    }
}

bool render_draw(DuiWinManager *mgr)
{
    render_start(mgr);
    auto hdc2 = CreateCompatibleDC((HDC)mgr->hdc);
    // HDC 必须有 BITMAP才能使用
    auto bmp = CreateCompatibleBitmap((HDC)mgr->hdc, mgr->rect.w, mgr->rect.h);
    SelectObject(hdc2, bmp);

    mgr->graph = new Gdiplus::Graphics(hdc2);
    mgr->graph->Clear(Color(255, 255, 255, 255));
    mgr->graph->SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
    //mgr->graph->SetTextRenderingHint(TextRenderingHintAntiAlias);
    //mgr->graph->SetTextRenderingHint(TextRenderingHintSingleBitPerPixel);

    // draw
    auto gp = mgr->graph;
    if (mgr->pRoot != nullptr)
    {
        auto pRoot = (UIContainer *)mgr->pRoot;
        _render_draw_child(mgr, pRoot);
    }

    // test code
    Pen pen(Color(150, 0, 0, 255), 6);
    gp->DrawLine(&pen, 10, 10, 150, 100);

    // copy
    BitBlt((HDC)mgr->hdc, 0, 0, mgr->rect.w, mgr->rect.h, hdc2, 0, 0, SRCCOPY);

    // copy
    DeleteObject(bmp);
    DeleteDC(hdc2);
    render_end(mgr);
    return true;
}

void render_draw_set_opacity(float opacity)
{
}

void render_draw_set_scale(float scale)
{
}

void render_draw_fill(DuiWinManager *mgr, DuiRect dest, DuiColor color, int radius)
{
    // 填充
    // https://blog.csdn.net/wwkaven/article/details/37575007
    SolidBrush brush(Color(color.a, color.r, color.g, color.b));
    GraphicsPath gpath;
    if (radius <= 0)
    {
        gpath.AddLine(dest.x, dest.y, dest.x + dest.w, dest.y);
        gpath.AddLine(dest.x + dest.w, dest.y, dest.x + dest.w, dest.y + dest.h);
        gpath.AddLine(dest.x + dest.w, dest.y + dest.h, dest.x, dest.y + dest.h);
        gpath.AddLine(dest.x, dest.y + dest.h, dest.x, dest.y);
    }
    else
    {
        int sr = std::min(std::min(dest.w, dest.h), radius);
        int sd = 2 * sr;
        gpath.AddArc(dest.x, dest.y, sd, sd, 180, 90);
        gpath.AddLine(dest.x + sr, dest.y, dest.x + dest.w - sr, dest.y);
        gpath.AddArc(dest.x + dest.w - sd, dest.y, sd, sd, 270, 90);
        gpath.AddLine(dest.x + dest.w, dest.y + sr, dest.x + dest.w, dest.y + dest.h - sr);
        gpath.AddArc(dest.x + dest.w - sd, dest.y + dest.h - sd, sd, sd, 0, 90);
        gpath.AddLine(dest.x + dest.w - sr, dest.y + dest.h, dest.x + sr, dest.y + dest.h);
        gpath.AddArc(dest.x, dest.y + dest.h - sd, sd, sd, 90, 90);
        gpath.AddLine(dest.x, dest.y + dest.h - sr, dest.x, dest.y + sr);
    }
    mgr->graph->FillPath(&brush, &gpath);
    // mgr->graph->DrawPath(&Pen(&brush), &gpath);
}

void render_draw_image(DuiWinManager *mgr, DuiRect dest, std::string image)
{
    mgr->graph->DrawImage((Image *)dui_comm_image_get(image), dest.x, dest.y, dest.w, dest.h);
}

void render_draw_image2(DuiWinManager *mgr, DuiRect dest, std::string image, DuiRect src)
{
    mgr->graph->DrawImage((Image *)dui_comm_image_get(image), RectF(dest.x, dest.y, dest.w, dest.h), src.x, src.y,
                          src.w, src.h, UnitPixel);
}

void render_draw_image_svg(DuiWinManager *mgr, DuiRect dest, std::string code, DuiColor color)
{
    // https://github.com/sammycage/plutosvg
}
void render_draw_border_image(DuiWinManager *mgr, DuiRect dest, DuiCorner corner, std::string image, DuiColor mask)
{
    // 九宫格方式
    // 四个角保持原样，其他区域 repeat
    auto img = (Image *)dui_comm_image_get(image);
    int sw = img->GetWidth();
    int sh = img->GetHeight();
    // 左上角
    mgr->graph->DrawImage(img, RectF(dest.x, dest.y, corner.r, corner.t), 0, 0, corner.r, corner.t, UnitPixel);
    TextureBrush brush1(img, Rect(corner.r, 0, sw - corner.r - corner.l, corner.t));
    mgr->graph->FillRectangle(&brush1, dest.x + corner.r, dest.y, dest.w - corner.r - corner.l, corner.t);

    // 右上角
    mgr->graph->DrawImage(img, RectF(dest.x + dest.w - corner.l, dest.y, corner.l, corner.t), sw - corner.l, 0,
                          corner.l, corner.t, UnitPixel);
    TextureBrush brush2(img, Rect(sw - corner.l, corner.t, corner.l, sh - corner.t - corner.b));
    mgr->graph->FillRectangle(&brush2, dest.x + dest.w - corner.l, dest.y + corner.t, corner.l,
                              dest.h - corner.t - corner.b);

    // 右下角
    mgr->graph->DrawImage(img, RectF(dest.x + dest.w - corner.l, dest.y + dest.h - corner.b, corner.l, corner.b),
                          sw - corner.l, sh - corner.b, corner.l, corner.b, UnitPixel);
    TextureBrush brush3(img, Rect(corner.r, sh - corner.b, sw - corner.l - corner.r, corner.b));
    mgr->graph->FillRectangle(&brush3, dest.x + corner.r, dest.y + dest.h - corner.b, dest.w - corner.l - corner.r,
                              corner.b);

    // 左下角
    mgr->graph->DrawImage(img, RectF(dest.x, dest.y + dest.h - corner.b, corner.r, corner.b), 0, sh - corner.b,
                          corner.r, corner.b, UnitPixel);
    TextureBrush brush4(img, Rect(0, corner.t, corner.r, sh - corner.t - corner.b));
    mgr->graph->FillRectangle(&brush4, dest.x, dest.y + corner.t, corner.r, dest.h - corner.t - corner.b);

    // 中间
    TextureBrush brush5(img, Rect(corner.r, corner.t, sw - corner.l - corner.r, sh - corner.t - corner.b));
    mgr->graph->FillRectangle(&brush5, dest.x + corner.r, dest.y + corner.t, dest.w - corner.r - corner.l,
                              dest.h - corner.t - corner.b);
}

void render_draw_shadow(DuiWinManager *mgr, DuiRect dest, DuiPoint args, int radius, DuiColor color,
                        DuiColor colorCenter)
{
    GraphicsPath gpath;
    if (radius <= 0)
    {
        gpath.AddLine(dest.x, dest.y, dest.x + dest.w, dest.y);
        gpath.AddLine(dest.x + dest.w, dest.y, dest.x + dest.w, dest.y + dest.h);
        gpath.AddLine(dest.x + dest.w, dest.y + dest.h, dest.x, dest.y + dest.h);
        gpath.AddLine(dest.x, dest.y + dest.h, dest.x, dest.y);
    }
    else
    {
        int sr = std::min(std::min(dest.w, dest.h), radius);
        int sd = 2 * sr;
        gpath.AddArc(dest.x, dest.y, sd, sd, 180, 90);
        gpath.AddLine(dest.x + sr, dest.y, dest.x + dest.w - sr, dest.y);
        gpath.AddArc(dest.x + dest.w - sd, dest.y, sd, sd, 270, 90);
        gpath.AddLine(dest.x + dest.w, dest.y + sr, dest.x + dest.w, dest.y + dest.h - sr);
        gpath.AddArc(dest.x + dest.w - sd, dest.y + dest.h - sd, sd, sd, 0, 90);
        gpath.AddLine(dest.x + dest.w - sr, dest.y + dest.h, dest.x + sr, dest.y + dest.h);
        gpath.AddArc(dest.x, dest.y + dest.h - sd, sd, sd, 90, 90);
        gpath.AddLine(dest.x, dest.y + dest.h - sr, dest.x, dest.y + sr);
    }
    // 计算阴影区域
    Matrix m;
    auto gpath2 = gpath.Clone();
    m.Translate(args.x, args.y);
    gpath2->Transform(&m);
    Region region(gpath2);
    region.Exclude(&gpath);

    // 渐变填色
    auto c1 = Color(color.a, color.r, color.g, color.b);
    auto c2 = Color(colorCenter.a, colorCenter.r, colorCenter.g, colorCenter.b);
    PathGradientBrush brush(gpath2);
    brush.SetCenterColor(c2);
    int count = 1;
    brush.SetSurroundColors(&c1, &count);
    brush.SetFocusScales(0.75f, 0.75f);
    mgr->graph->FillRegion(&brush, &region);
}

void render_draw_point(DuiWinManager *mgr, DuiPoint point, DuiColor color, int size)
{
    Pen brush(Color(color.a, color.r, color.g, color.b));
    mgr->graph->DrawEllipse(&brush, point.x, point.y, size, size);
}

void render_draw_line(DuiWinManager *mgr, DuiPoint p1, DuiPoint p2, DuiColor color, int width, DUI_LINE_STYLE style)
{
    Pen pen(Color(color.a, color.r, color.g, color.b), width);
    pen.SetDashStyle((DashStyle)style);
    mgr->graph->DrawLine(&pen, p1.x, p1.y, p2.x, p2.y);
}

void render_draw_rect(DuiWinManager *mgr, DuiRect dest, DuiColor color, int width, DUI_LINE_STYLE style, int radius)
{
    Pen pen(Color(color.a, color.r, color.g, color.b), width);
    pen.SetDashStyle((DashStyle)style);
    GraphicsPath gpath;
    if (radius <= 0)
    {
        gpath.AddLine(dest.x, dest.y, dest.x + dest.w, dest.y);
        gpath.AddLine(dest.x + dest.w, dest.y, dest.x + dest.w, dest.y + dest.h);
        gpath.AddLine(dest.x + dest.w, dest.y + dest.h, dest.x, dest.y + dest.h);
        gpath.AddLine(dest.x, dest.y + dest.h, dest.x, dest.y);
    }
    else
    {
        int sr = std::min(std::min(dest.w, dest.h), radius);
        int sd = 2 * sr;
        gpath.AddArc(dest.x, dest.y, sd, sd, 180, 90);
        gpath.AddLine(dest.x + sr, dest.y, dest.x + dest.w - sr, dest.y);
        gpath.AddArc(dest.x + dest.w - sd, dest.y, sd, sd, 270, 90);
        gpath.AddLine(dest.x + dest.w, dest.y + sr, dest.x + dest.w, dest.y + dest.h - sr);
        gpath.AddArc(dest.x + dest.w - sd, dest.y + dest.h - sd, sd, sd, 0, 90);
        gpath.AddLine(dest.x + dest.w - sr, dest.y + dest.h, dest.x + sr, dest.y + dest.h);
        gpath.AddArc(dest.x, dest.y + dest.h - sd, sd, sd, 90, 90);
        gpath.AddLine(dest.x, dest.y + dest.h - sr, dest.x, dest.y + sr);
    }
    mgr->graph->DrawPath(&pen, &gpath);
}

void render_draw_arc(DuiWinManager *mgr, DuiRect dest, DuiColor color, int width, DUI_LINE_STYLE style, int angle1,
                     int angle2)
{
    Pen pen(Color(color.a, color.r, color.g, color.b), width);
    pen.SetDashStyle((DashStyle)style);
    mgr->graph->DrawArc(&pen, dest.x, dest.y, dest.w, dest.h, angle1, angle2);
}
void render_draw_text(DuiWinManager *mgr, DuiPoint point, std::string &text, DuiColor color, std::string fontFamily,
                      int fontSize, int fontWeight)
{
    wchar_t text2[1024 * 10];
    auto len = utf8_to_utf16(text.c_str(), text2, 1024);
    std::wstring text3(text2, len);
    render_draw_text(mgr, point, text3, color, fontFamily, fontSize, fontWeight);
}
void render_draw_text(DuiWinManager *mgr, DuiPoint point, std::wstring &text, DuiColor color, std::string fontFamily,
                      int fontSize, int fontWeight)
{
    auto fontStyle = FontStyleRegular;
    if (fontWeight > 400)
    {
        fontStyle = FontStyleBold;
    }
    Font font((Gdiplus::FontFamily *)dui_comm_font_get(fontFamily), fontSize, fontStyle, UnitPixel);

    SolidBrush brush(Color(color.a, color.r, color.g, color.b));
    mgr->graph->DrawString(text.c_str(), text.length(), &font, PointF(point.x, point.y), &brush);
}

void render_draw_text(DuiWinManager *mgr, DuiRect rect, std::string &text, DuiColor color, DUI_TEXT_ALIGN alignx,
                      DUI_TEXT_ALIGN aligny, std::string fontFamily, int fontSize, int fontWeight)
{
    wchar_t text2[1024 * 10];
    auto len = utf8_to_utf16(text.c_str(), text2, 1024 * 10);
    std::wstring text3(text2, len);
    render_draw_text(mgr, rect, text3, color, alignx, aligny, fontFamily, fontSize, fontWeight);
}
void render_draw_text(DuiWinManager *mgr, DuiRect rect, std::wstring &text, DuiColor color, DUI_TEXT_ALIGN alignx,
                      DUI_TEXT_ALIGN aligny, std::string fontFamily, int fontSize, int fontWeight)
{
    // https://www.cnblogs.com/mowwwcom/p/15998743.html
    auto fontStyle = FontStyleRegular;
    if (fontWeight > 400)
    {
        fontStyle = FontStyleBold;
    }
    Font font((Gdiplus::FontFamily *)dui_comm_font_get(fontFamily), fontSize, fontStyle, UnitPixel);

    SolidBrush brush(Color(color.a, color.r, color.g, color.b));
    StringFormat fmt;
    fmt.SetLineAlignment((Gdiplus::StringAlignment)aligny);
    fmt.SetAlignment((Gdiplus::StringAlignment)alignx);
    mgr->graph->DrawString(text.c_str(), text.length(), &font, RectF(rect.x, rect.y, rect.w, rect.h), &fmt, &brush);
}

DuiRect render_measure_text(DuiWinManager *mgr, std::wstring &text, std::string fontFamily, int fontSize,
                            int fontWeight)
{
    auto fontStyle = FontStyleRegular;
    if (fontWeight > 400)
    {
        fontStyle = FontStyleBold;
    }
    GraphicsPath path;
    auto pFontFam = (Gdiplus::FontFamily *)dui_comm_font_get(fontFamily);
    Font font(pFontFam, fontSize, fontStyle, UnitPixel);
    StringFormat sfmt;
    sfmt.SetAlignment(StringAlignmentNear);
    sfmt.SetLineAlignment(StringAlignmentNear);
    path.AddString(text.c_str(), text.length(), pFontFam, font.GetStyle(), font.GetSize(), Point(0, 0), &sfmt);
    Rect rect;
    path.GetBounds(&rect);

    return DuiRect{rect.X, rect.Y, rect.Width, rect.Height};
}

std::list<DuiCharLineInf> render_measure_parse_text(DuiWinManager *mgr, std::wstring &text, Gdiplus::Font &font, Gdiplus::Font &fontCb,
                                                    Gdiplus::StringFormat &fmt, DuiRect &rc)
{
    auto ilines = std::list<DuiCharLineInf>();
    FontFamily fontfm[2];
    font.GetFamily(&fontfm[0]);
    fontCb.GetFamily(&fontfm[1]);
    RectF rect;
    wchar_t buff[2];
    byte buff_len = 0;
    auto iline = std::list<DuiCharInf>();
    int iline_w = 0;
    for (int i = 0; i < text.length(); i++)
    {
        buff[0] = text[i];
        if (buff[0] >= 0xD800)
        {
            buff[1] = text[i + 1];
            i++;
            buff_len = 2;
        }
        else
        {
            buff_len = 1;
        }
        GraphicsPath path;
        if(buff_len>1){
            path.AddString(buff, buff_len, &fontfm[1], fontCb.GetStyle(), fontCb.GetSize(), Point(0, 0), &fmt);
        }else{
            path.AddString(buff, buff_len, &fontfm[0], font.GetStyle(), font.GetSize(), Point(0, 0), &fmt);
        }
        path.GetBounds(&rect);

        //mgr->graph->MeasureString(buff, buff_len, &font, PointF(0, 0), &rect);
        // add char
        DuiCharInf cinf;
        cinf.c[0] = buff[0];
        cinf.c[1] = buff[1];
        cinf.len = buff_len;
        cinf.w = rect.Width + rect.X;
        cinf.h = rect.Height;
        cinf.x = rect.X;
        cinf.y = rect.Y;

        // 特殊字符处理
        if( cinf.c[0] == ' ' ){
            cinf.w = font.GetSize() / 3;
        }else
        if( cinf.c[0] == '\t' ){
            cinf.w = font.GetSize() * 1.3;
        }
        
        // 换行
        if ((iline_w <= rc.w && (iline_w + cinf.w) > rc.w) || (cinf.c[0] == '\n') )
        {
            // 新行
            ilines.push_back(DuiCharLineInf{0, iline});
            iline = std::list<DuiCharInf>();
            iline_w = 0;
        }

        iline.push_back(cinf);
        iline_w += cinf.w;
    }
    // 最后一行
    if (iline.size() > 0)
    {
        ilines.push_back(DuiCharLineInf{0, iline});
    }

    return ilines;
}
